#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>

namespace ns_thread_pool
{
    const size_t NUM = 5;
    // 线程池只限制线程数，不限制任务数
    template <class T>
    class ThreadPool
    {
    private:
        std::queue<T> _q;
        int _num;

        pthread_mutex_t _mtx;
        pthread_cond_t _cond;

        static ThreadPool<T>* ins;

    private:
    // 单例模式中，为了只创建一个实例，必须有且只有一个私有的构造函数
        ThreadPool()
            :_num(NUM)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_cond, nullptr);
        }

        ThreadPool(const ThreadPool<T>& tp) = delete;

        const ThreadPool<T>& operator=(const ThreadPool<T>& tp) = delete;


        void Lock()
        {
            pthread_mutex_lock(&_mtx);
        }

        void Unlock()
        {
            pthread_mutex_unlock(&_mtx);
        }

        void Wait()
        {
            pthread_cond_wait(&_cond, &_mtx);
        }

        void WakeUp()
        {
            pthread_cond_signal(&_cond);
        }

        bool IsEmpty()
        {
            return _q.empty();
        }

        void InitThreadPool()
        {
            pthread_t pid;
            for(int i = 0; i < _num; ++i)
            {
                pthread_create(&pid, nullptr, Rountine, (void*)this);
            }
        }

    public:
        static ThreadPool<T>* GetInstance()
        {
            static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

            if(ins == nullptr)
            {
                pthread_mutex_lock(&mtx);
                if(ins == nullptr)
                {
                    ins = new ThreadPool<T>();
                    ins->InitThreadPool();
                    std::cout << "实例首次被创建" << std::endl;
                }
                pthread_mutex_unlock(&mtx);
            }
            return ins;
        }

        ~ThreadPool()
        {
            pthread_mutex_destroy(&_mtx);
            pthread_cond_destroy(&_cond);
        }

        void Pop(T* data)
        {
            *data = _q.front();
            _q.pop();
        }

        void Push(const T& data)
        {
            Lock();
            _q.push(data);
            Unlock();
            WakeUp();
        }

        static void* Rountine(void* args)
        {
            pthread_detach(pthread_self());
            ThreadPool<T>* tp = (ThreadPool<T>*)args;

            while(true)
            {
                tp->Lock();
                while(tp->IsEmpty())
                {
                    tp->Wait();
                }

                T t;
                tp->Pop(&t);
                tp->Unlock();
                t();
            }
        }
    };

    template <class T>
    ThreadPool<T>* ThreadPool<T>::ins = nullptr;
}